#ifndef _MEMORIACOMPARTIDA_H_
#define _MEMORIACOMPARTIDA_H_

#define SHM_OK			0
#define	ERROR_FTOK		-1
#define ERROR_SHMGET	-2
#define	ERROR_SHMAT		-3
#define	ERROR_CREATED	-4

#include	<sys/types.h>
#include	<sys/ipc.h>
#include	<sys/shm.h>
#include 	<cstdlib>

template <class T> class SharedMemory {

private:

	int 	shmId;
	T*		ptrDatos;



public:
	int attachedProcessCount ();
	SharedMemory ();
	~SharedMemory ();
	/* archivo: path de un archivo existente
	 * letra: un caracter cualquiera
	 *
	 * Deben ser iguales para todos los procesos que quieran compartir la misma memoria
	 * */
	int create ( const char *archivo, char letra );
	void free ();

	void write ( T *dato );

	void write ( T dato );

	T read ();

	T* get ();

};


template <class T>
SharedMemory<T> :: SharedMemory () {
	this->ptrDatos = NULL;
	this->shmId = -1;
}

template <class T>
SharedMemory<T> :: ~SharedMemory () {
}

template <class T>
int SharedMemory<T> :: create ( const char *archivo, char letra ) {

	// generacion de la clave
	if ( this->ptrDatos != NULL )
		return ERROR_CREATED;

	key_t clave = ftok ( archivo,letra );
	if ( clave == -1 )
		return ERROR_FTOK;

	// creacion de la memoria compartida
	this->shmId = shmget ( clave,sizeof(T),0660|IPC_CREAT );

	if ( this->shmId == -1 )
		return ERROR_SHMGET;
	else {
		// attach del bloque de memoria al espacio de direcciones del proceso
		void* ptrTemporal = shmat ( this->shmId,NULL,0 );

		if ( ptrTemporal == (void *) -1 ) {
			return ERROR_SHMAT;
		} else {
			this->ptrDatos = (T *) ptrTemporal;
			return SHM_OK;
		}
	}
}


template <class T>
void SharedMemory<T> :: free () {
	// detach del bloque de memoria
	shmdt ( (void *) this->ptrDatos );

	int procAdosados = this->attachedProcessCount ();

	if ( procAdosados == 0 ) {
		shmctl ( this->shmId,IPC_RMID,NULL);
		this->ptrDatos = NULL;
		this->shmId = -1;
	}
}

template <class T>
void SharedMemory<T> :: write ( T *dato ) {
	*(this->ptrDatos) = (*dato);
}

template <class T>
void SharedMemory<T> :: write ( T dato ) {
	*(this->ptrDatos) = dato;
}

template <class T>
T SharedMemory<T> :: read () {
	return ( *(this->ptrDatos) );
}

template <class T>
T* SharedMemory<T> :: get () {
	return this->ptrDatos;
}


template <class T>
int SharedMemory<T> :: attachedProcessCount () {
	shmid_ds estado;
	shmctl ( this->shmId,IPC_STAT,&estado );
	return estado.shm_nattch;
}


#endif /* _MEMORIACOMPARTIDA_H_ */
